package com.anlogic.sdk.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.logging.log4j.Logger;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;

public class JsonUtils {
	private static Logger logger = LogUtils.getLogger(JsonUtils.class);

	public static boolean isJsonString(JsonElement element) {
		return (element.isJsonPrimitive() && element.getAsJsonPrimitive().isString());
	}

	public static String getAsString(JsonElement element) {
		if (element != null && element.isJsonArray()) {
			JsonArray array = element.getAsJsonArray();
			boolean isStringArray = true;
			for (JsonElement e : array)
				isStringArray &= isJsonString(e);
			if (isStringArray) {
				StringBuilder sb = new StringBuilder();
				for (JsonElement e : array) {
					if (isJsonString(e)) {
						if (sb.length() > 0)
							sb.append("\n");
						sb.append(e.getAsString());
					}
				}
				return sb.toString();
			}
			return element.toString();
		}
		if (element != null && isJsonString(element))
			return element.getAsString();
		if (element != null && element.isJsonPrimitive()) {
			JsonPrimitive primitive = element.getAsJsonPrimitive();
			if (primitive.isBoolean())
				return primitive.getAsBoolean() ? "true" : "false";
			return primitive.toString();
		}
		if (element != null) {
			String maybeString = element.toString();
			return maybeString;
		}
		return null;
	}

	public static String getJsonString(JsonObject object, String id) {
		JsonElement element = object.get(id);
		return getAsString(element);
	}

	public static String getNonNullJsonString(JsonObject object, String id) {
		JsonElement element = object.get(id);
		String s = getAsString(element);
		return (s == null) ? "" : s;
	}

	public static JsonArray getJsonArray(JsonElement element, String id) {
		if (element.isJsonObject()) {
			JsonObject jsonObject = element.getAsJsonObject();
			JsonElement object = jsonObject.get(id);
			if (object != null && object.isJsonArray())
				return object.getAsJsonArray();
		}
		return null;
	}
	
	public static JsonObject getJsonObject(JsonElement element, String id) {
		if (element != null && element.isJsonObject()) {
			JsonObject obj = element.getAsJsonObject();
			JsonElement value = getJsonElement(obj, id);
			if (value != null && value.isJsonObject())
				return value.getAsJsonObject();
		}
		return null;
	}
	
	public static JsonElement getJsonElement(JsonObject object, String idStr, String delimiter) {
		String[] ids = idStr.split(delimiter);
		
		JsonObject tempObj = object;
		JsonElement ret = null;
		for (String id : ids) {
			ret = tempObj.get(id);
			if (ret != null && ret.isJsonObject()) {
				tempObj = ret.getAsJsonObject();
			}
		}

		return ret;
	}

	public static JsonElement getJsonElement(JsonObject object, String id) {
		JsonElement value = object.get(id);
		if (value != null && value.isJsonArray()) {
			JsonArray array = value.getAsJsonArray();
			if (array.size() == 1)
				value = array.get(0);
		}
		return value;
	}

	public static List<String> getStringList(JsonObject object, String id) {
		return getStringList(object, id, false);
	}

	public static List<String> getStringList(JsonObject object, String id, boolean allowEmptyStrings) {
		List<String> list = new ArrayList<>();
		JsonElement child = object.get(id);
		if (child != null && child.isJsonPrimitive()) {
			String val = child.getAsString();
			if (val != null && !val.isEmpty())
				list.add(val);
		} else {
			JsonArray array = getJsonArray((JsonElement) object, id);
			if (array != null)
				for (JsonElement e : array) {
					String s = getAsString(e);
					if (s != null && (!s.isEmpty() || allowEmptyStrings))
						list.add(s);
				}
		}
		return list;
	}
	
	public static JsonObject readJson(String string) {
		JsonObject jsonObject = null;
		try {
			jsonObject = JsonParser.parseString(string).getAsJsonObject();
		} catch (JsonIOException | JsonSyntaxException e) {
			logger.error("Parse json string failed {}", e.getMessage());
			jsonObject = new JsonObject();
		}
		return jsonObject;
	}

	public static List<String> readJsonArray(String value) {
		List<String> values = new ArrayList<>();
		JsonElement json = readJson(value);
		if (json != null)
			if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) {
				values.add(json.getAsString());
			} else if (json.isJsonArray()) {
				JsonArray array = json.getAsJsonArray();
				for (JsonElement element : array) {
					if (element != null && element.isJsonPrimitive()) {
						JsonPrimitive prim = (JsonPrimitive) element;
						if (prim.isString()) {
							String s = prim.getAsString();
							values.add(s);
						}
					}
				}
			}
		return values;
	}

	public static JsonObject readJsonFile(File f) {
		JsonObject jsonObject = null;
		InputStreamReader reader = null;
		try {
			reader = new InputStreamReader(new FileInputStream(f));
			jsonObject = JsonParser.parseReader(reader).getAsJsonObject();
			reader.close();
			reader = null;
		} catch (JsonIOException | JsonSyntaxException e) {
			logger.error("Read json file '{}' failed {}", f.getAbsolutePath(), e.getMessage());
			jsonObject = new JsonObject();
		} catch (IOException iOException) {
			jsonObject = new JsonObject();
		} finally {
			if (reader != null)
				try {
					reader.close();
				} catch (IOException iOException) {}
		}
		return jsonObject;
	}

	public static JsonObject readJsonFile(Reader reader) {
		JsonObject jsonObject = null;
		try {
			jsonObject = JsonParser.parseReader(reader).getAsJsonObject();
			reader.close();
		} catch (JsonIOException | JsonSyntaxException e) {
			logger.error("Read json file failed: {}", e.getMessage());
			jsonObject = new JsonObject();
		} catch (IOException iOException) {
			jsonObject = new JsonObject();
		} finally {
			if (reader != null)
				try {
					reader.close();
				} catch (IOException iOException) {}
		}
		return jsonObject;
	}
	
	public static <T> T parse(String json, Class<T> clazz) {
		T x = null;
		try {
			Gson gson = new Gson();
			x = (T) gson.fromJson(json, clazz);
		} catch (Exception e) {
			logger.error("Parse json failed: {}", e.getMessage());
		}
		return x;
	}

	public static <T> T read(File f, Class<T> clazz) {
		String json = FileUtils.read(f);
		if (json != null)
			return parse(json, clazz);
		return null;
	}

	public static void write(File f, Object o) {
		Gson gson = (new GsonBuilder()).setPrettyPrinting().create();
		String json = null;
		if (o instanceof String) {
			json = gson.toJson(JsonParser.parseString((String)o));
		} else {
			json = gson.toJson(o);
		}
		FileWriter writer = null;
		try {
			writer = new FileWriter(f);
			writer.write(json);
			writer.flush();
			writer.close();
			writer = null;
		} catch (IOException e) {
			logger.error("Write json file '{}' failed: {}", f.getAbsolutePath(), e.getMessage());
		} finally {
			if (writer != null)
				FileUtils.closeQuietly(writer);
		}
	}
	
	public static boolean getBoolean(JsonObject object, String name, boolean defaultValue) {
		boolean value = defaultValue;
		JsonElement e = object.get(name);
		if (e != null) {
			String s = e.getAsString();
			if (s != null && !s.isEmpty())
				value = equalsTrue(s);
		}
		return value;
	}

	private static boolean equalsTrue(String value) {
		String[] trueValues = { "true", "yes", "1", "on" };
		List<String> truthiness = Arrays.asList(trueValues);
		return (value != null && truthiness.contains(value.toLowerCase()));
	}
	
	public static String toJson(Object o) {
		Gson gson = (new GsonBuilder()).setPrettyPrinting().create();
		String json = gson.toJson(o);
		return json;
	}
}